// 
// FileManager.cs
//  
// Author:
//       Tomas Vondracek <tomas.vondracek@gmail.com>
// 
// Copyright (c) 2009 Tomas Vondracek
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
//using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace ZVI
{


	public class FileService
	{
		string tempDir;
		public string TempDir
		{
			get { return tempDir; }
			set { tempDir = value; }
		}

        string currentImagePath;

        public string CurrentImagePath
        {
            get { return currentImagePath; }
        }

		int maxTempSize;

		/// <summary>
		///size of temp folder in MB 
		/// </summary>
		public int MaxTempSize
		{
			get { return maxTempSize; }
			set { maxTempSize = value; }
		}

		uint operationCounter;
		
		const string LOCK_FILENAME = ".lock";

		static FileService instance;

		/// <summary>
		/// this class is singleton
		/// </summary>
		/// <returns>
		/// A <see cref="FileManager"/>
		/// </returns>
		public static FileService GetInstance ()
		{
			if (instance == null)
				instance = new FileService ();
			
			return instance;
		}

		private FileService ()
		{
			//Create temp directory for storing temporal images
			string path = System.IO.Path.Combine (System.Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), "histogram");
			DirectoryInfo dir = Directory.CreateDirectory (path + System.IO.Path.DirectorySeparatorChar + "temp");
			
			this.tempDir = dir.FullName;
			
			this.operationCounter = 0;
			this.maxTempSize = 60;
			
			Application.ApplicationExit += HandleApplicationExit;
			
			CreateLockFile();
		}
		
		/// <summary>
		/// creates log file to be able to find out if application is already running
		/// </summary>
		private void CreateLockFile()
		{
			string path = this.GetDefaultSettingsPath() + LOCK_FILENAME;
			
			if (File.Exists(path))
			{
				Logging.Logger.AddError("Lock file already exists!");
				#if !DEBUG
				MessageBox.Show("Lock file already exists. This means, that application is already running or was not properly closed.\n" + 
				                "If the application is already running, be aware, that this can couse serious troubles, even loss of data!", 
				                "Lock file exists!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				#endif
			}
			else
			{
				try
				{
					File.Create(path);
					Logging.Logger.AddInfo("Created lock file.");
				}
				catch (Exception ex)
				{
					Logging.Logger.AddError(ex.ToString());
					MessageBox.Show("Exception while creating lock file! " + ex.ToString(), "Can't delete lock file!",
					                MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
		}

		/// <summary>
		/// quiting, release lock file
		/// </summary>
		/// <param name="sender">
		/// A <see cref="System.Object"/>
		/// </param>
		/// <param name="e">
		/// A <see cref="EventArgs"/>
		/// </param>
		private void HandleApplicationExit (object sender, EventArgs e)
		{
			Logging.Logger.AddInfo("FileService is quiting!");
			
			string path = this.GetDefaultSettingsPath() + LOCK_FILENAME;
			
			if (!File.Exists(path))
			{
				Logging.Logger.AddError("Lock file does not exists!");
			}
			else
			{
				try
				{
					File.Delete(path);
					Logging.Logger.AddInfo("Deleted lock file.");
				}
				catch (Exception ex)
				{
					Logging.Logger.AddError(ex.ToString());
					MessageBox.Show("Exception while deleting lock file! " + ex.ToString(), "Can't delete lock file!",
					                MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
		}

        /// <summary>
        /// save image to temp dir, for later use in undo redo system
        /// </summary>
        /// <param name="img">image to save</param>
        /// <returns>path where the image was saved to</returns>
		public string SaveTempImage (Image img)
		{
			string path = null;
			try
			{
				string dir = this.tempDir + Path.DirectorySeparatorChar;
				path = dir + "zvi-" + this.operationCounter + "-" + 
					DateTime.Now.ToString ("yyyy'-'MM'-'dd'T'HH'_'mm'_'ss") + ".bmp";
				//Logging.Logger.AddInfo ("Saving TempImage: " + path);
				
				//check if directory exists - it could change by settings or external stuff
				if (!Directory.Exists(dir))
				{
					Directory.CreateDirectory(dir);
				}
				
				lock (Threading.SyncObject.GetInstance ())
				{
					//this.pictureBox1.Image.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
					img.Save (path, System.Drawing.Imaging.ImageFormat.Bmp);
				}
				
				this.operationCounter++;
			}
			catch (Exception ex)
			{
				Logging.Logger.AddError (ex.ToString ());
				MessageBox.Show ("There was error saving temp image, some functions might be unavailable (undo/redo)", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
            img = null;
			return path;
		}

        /// <summary>
        /// save origin image - it can be used later to return
        /// </summary>
        /// <param name="img"></param>
		public void SaveOriginImage (Image img)
		{
            try
            {
                string dir = this.tempDir + Path.DirectorySeparatorChar;
                string path = dir + "zvi-original.bmp";

                //check if file exists and if directory exists - it could change by settings or external stuff
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                else if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                //Logging.Logger.AddInfo ("Saving original Image");

                lock (Threading.SyncObject.GetInstance())
                {
                    img.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
                }
            }
            catch (Exception ex)
            {
                Logging.Logger.AddError(ex.ToString());
                MessageBox.Show("There was error saving original temp image, some functions might be unavailable", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                img = null;
            }
		}

        /// <summary>
        /// saves the image to disk, the image type is determined by the extension
        /// </summary>
        /// <param name="img">image to save</param>
        /// <param name="path">full path to image</param>
        public void SaveImage(Image img, string path)
        {
            Logging.Logger.AddInfo("Saving Image "+ path);
            try
            {
                FileInfo info = new FileInfo(path);
                string dir = info.DirectoryName;
                //check if file exists and if directory exists - it could change by settings or external stuff
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                else if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                string extension = path.Substring(path.LastIndexOf(".") + 1);

                switch (extension.ToLowerInvariant())
                {
                    case "jpg":
                    case "jpeg":
                        img.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
                        break;
                    case "bmp":
                        img.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                    case "png":
                        img.Save(path, System.Drawing.Imaging.ImageFormat.Png);
                        break;
                    default:
                        string message = string.Format("unrecognized file format {0}; Image was not saved! ", extension);
                        Logging.Logger.AddWarning(message);
                        MessageBox.Show(message, "Save error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        break;
                }
                currentImagePath = path;
            }
            catch (Exception ex)
            {
                string message = string.Format("There was an error while saving image {0}\n {1} ", path, ex.Message);
                Logging.Logger.AddException(ex);
                MessageBox.Show(message, "Save error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        
            }
            finally
            {
                img = null;
            }
        }

        /// <summary>
        /// loads image from file on disk
        /// </summary>
        /// <param name="path">full path to image</param>
        /// <param name="isOrigin">determine wheter the image is considered as origin</param>
        /// <returns>loaded image</returns>
        /// <remarks>
        /// see http://support.microsoft.com/kb/814675/en-us
        /// </remarks>
        public Image LoadImage(string path, bool isOrigin)
        {
            Bitmap loadedImage = null;

            Logging.Logger.AddImportant("Going to load Image! Path: " + path);

            try
            {

                Bitmap bmp = new Bitmap(path);
                loadedImage = new Bitmap(bmp.Width, bmp.Height);

                //this is neccessary, see
                // http://support.microsoft.com/kb/814675/en-us
                loadedImage = new Bitmap(bmp.Clone() as Bitmap);

                bmp.Dispose();

                if (isOrigin)
                {
                    currentImagePath = path; 
                }
            }
            catch (Exception ex)
            {
                Logging.Logger.AddError(ex.ToString());
                MessageBox.Show("Image load failed..." + ex.GetBaseException().TargetSite, ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            return loadedImage;
        }

        /// <summary>
        /// calculates the directory size
        /// </summary>
        /// <param name="p">full path to teh direcotry</param>
        /// <returns>calculated size in bytes</returns>
		public long GetDirectorySize (string p)
		{
			// Get array of all file names.
            if (!Directory.Exists(p))
            {
                Logging.Logger.AddWarning("GetDirectirySize: The directory does not exists! " + p); 
                return 0;
            }
			string[] a = Directory.GetFiles (p, "*.*");
			
			// Calculate total bytes of all files in a loop.
			long b = 0;
			try
			{
				foreach (string name in a)
				{
					// Use FileInfo to get length of each file.
					FileInfo info = new FileInfo (name);
					b += info.Length;
				}
			}
			catch (Exception ex)
			{
				Logging.Logger.AddError(ex.ToString());
			}
			// Return total size
			return b;
		}

		/// <summary>
		/// delete all files in temp dir
		/// </summary>
		public void CleanUpTempDir ()
		{
			Logging.Logger.AddInfo ("cleaning files in " + this.tempDir);
			string[] files = Directory.GetFiles (this.tempDir);
			
			try
			{
				foreach (var file in files)
				{
					File.Delete (file);
				}
			}
			catch (Exception ex)
			{
				string message = string.Format ("{0} while deleting temp files. Check folder {1} for temp files. \n Details: {2}", ex.Message, this.tempDir, ex.ToString ());
				Logging.Logger.AddError (message);
				
				MessageBox.Show (message, "Error deleting temp files!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
			}
		}
		
		/// <summary>
		/// gets a full folder path, where are stored all settings
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> a full folder path, filename is not included
		/// </returns>
		public string GetDefaultSettingsPath()
		{
			string path = System.IO.Path.Combine (System.Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), "histogram");
			return path + System.IO.Path.DirectorySeparatorChar;
		}
		

        /// <summary>
        /// default filename for settings file
        /// </summary>
        /// <returns></returns>
		public string GetDefautSettingsFileName()
		{
			return "histogram.conf";
		}
		
		/// <summary>
		/// default path for compiled plugins,
		/// form this path are plugins loaded by default
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/>
		/// </returns>
		public string GetDefaultPluginsPath()
		{
			string path = Path.Combine(Application.StartupPath, "Plugins") + Path.DirectorySeparatorChar;
			return path;
		}
	}
}
